/*
@hehung
2023-2-8
转载请注明出处，版权由@hehung所有
email: 1398660197@qq.com
wechat: hehung95
*/

#include "hal_data.h"
#include "app_key.h"
#include "app_led.h"
#include "app_flash_hex.h"


#define FLASH_WRITE_MASK        (FLASH_WRITE_SIZE-1)
#define RAM_AREA_START          (0x1FFE0000)
#define OSIS_CONFIG_AREA        (0x0100A150)    /* OCD/Serial Programmer ID Setting Register (OSIS) */


static flash_write_cache_t fcache_info;

uint32_t FlashHex_StartProgramming(void)
{
    fsp_err_t status;
    flash_info_t flash_info;
    flash_result_t result;
    uint32_t address;

#if 0
    status = g_flash0.p_api->open(g_flash0.p_ctrl, g_flash0.p_cfg);
    if (status) __BKPT();
#endif
    status = g_flash0.p_api->infoGet(g_flash0.p_ctrl, &flash_info);
    if (status) __BKPT();

    fcache_info.dirty = false;
    fcache_info.start_address = FLASH_CODE_AREA_START;
    fcache_info.next_address   = FLASH_CODE_AREA_START + FLASH_WRITE_SIZE;
    memset(fcache_info.write_block, 0xFF, sizeof(fcache_info.write_block));

    for (address = FLASH_CODE_AREA_START; address < FLASH_CODE_AREA_END; address += FLASH_BLOCK_SIZE)
    {
        status = g_flash0.p_api->blankCheck(g_flash0.p_ctrl, address, FLASH_BLOCK_SIZE, &result);
        if  (status) __BKPT();
        if (FLASH_RESULT_NOT_BLANK == result)
        {
			// erase the flash
           __disable_irq();
           status = g_flash0.p_api->erase(g_flash0.p_ctrl, address, 1);
           if (status) __BKPT();
           __enable_irq();
        }
    }

    return(0);
}

void write_flash(uint32_t addr, uint8_t * pData, uint32_t len)
{
    fsp_err_t status;
    uint32_t i;
    uint8_t idx;

    if (addr < fcache_info.start_address) __BKPT();     // We can't re-program flash without erasing it!!!

    /*
     *  The address we are trying to program is outside the range of the current write line
     */
    if (addr >= fcache_info.next_address)
    {
        if (fcache_info.dirty)        // Flush the current buffer
        {
            __disable_irq();
            status = g_flash0.p_api->write(g_flash0.p_ctrl, (uint32_t)fcache_info.write_block, fcache_info.start_address, FLASH_WRITE_SIZE);
            __enable_irq();
            if (status) __BKPT();
        }
        memset(fcache_info.write_block, 0xFF, FLASH_WRITE_SIZE);
        fcache_info.start_address = addr & (~FLASH_WRITE_MASK);
        fcache_info.next_address = fcache_info.start_address + FLASH_WRITE_SIZE;
    }

    /*
     *  The address is within the current write line
     */
    for (i = 0, idx = (addr & FLASH_WRITE_MASK); i<len; i++, idx++)
    {
        /*
         *  If the address is outside the write line flush the buffer first then fill the buffer
         */
        if ((i+addr) == fcache_info.next_address)
        {
            __disable_irq();
            status = g_flash0.p_api->write(g_flash0.p_ctrl, (uint32_t) fcache_info.write_block, fcache_info.start_address, FLASH_WRITE_SIZE);
            __enable_irq();
            if (status) __BKPT();

            memset(fcache_info.write_block, 0xFF, FLASH_WRITE_SIZE);
            fcache_info.start_address = fcache_info.next_address;
            fcache_info.next_address =  fcache_info.start_address + FLASH_WRITE_SIZE;
            idx = 0;
        }
        /*
         *  The address is within the write line, fill the write buffer
         */
        fcache_info.write_block[idx] = pData[i];
     }
     fcache_info.dirty = true;
}
